# -*- coding: utf-8 -*-
import re
from functools import wraps

import geoip2.database
from django.conf import settings
from django.utils.encoding import smart_unicode
from django.views.decorators.csrf import csrf_exempt
from future.utils import raise_with_traceback

from common.utils.exceptions import AuthenticateError, ParamError
from common.utils.respcode import StatusCode

DEFAULT_COUNTRY = "OTHER"
_DEFAULT_PAGE_SIZE = 10
_MAX_PAGE_SIZE = 20


def check_params(params, required_params,  # 必须值
                 default_param_dct=None,  # 默认值
                 param_type_dct=None,  # 参数类型，GET一般需要强制转换
                 param_validfunc_dct=None):  # 参数合法判定
    '''验证传入参数有效性，注意这里会对params做in-place修改
    '''
    for param in required_params:
        if param not in params:
            raise_with_traceback(ParamError('missing %s' % param))

    if default_param_dct:
        for param in default_param_dct:
            if param not in params:
                params[param] = default_param_dct[param]

    if param_type_dct:
        for field, t in param_type_dct.iteritems():
            if field in params:
                try:
                    if t is basestring:
                        t = smart_unicode
                    params[field] = t(params[field])
                except Exception:
                    raise_with_traceback(ParamError(
                        'param %s type wrong' % field))

    if param_validfunc_dct:
        for field, func in param_validfunc_dct.iteritems():
            if field in params:
                try:
                    assert func(params[field])
                except AssertionError:
                    raise_with_traceback(ParamError(
                        'param %s illegal' % field))


def check_auth(req):
    if not req.user_id:
        raise AuthenticateError(status=StatusCode.INVALID_TOKEN)


# for class based view, use `method_decorator`
def token_required(func):
    @csrf_exempt
    @wraps(func)
    def _wrapped(req, *args, **kwargs):
        check_auth(req)
        return func(req, *args, **kwargs)

    return _wrapped


def get_client_ip(request):
    ip = None
    real_ip = request.META.get('HTTP_X_REAL_IP')
    if real_ip:
        ip = real_ip.split(',')[0]
    if not ip or ip == "unknown":
        ip = request.META.get('REMOTE_ADDR')
    return ip


class Singleton(object):
    # 记录第一个被创建对象的引用
    instance = None
    # 记录是否执行过初始化动作
    flag = False

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            # 调用父类的方法，为第一个对象分配空间
            cls.instance = super(Singleton, cls).__new__(cls)

        return cls.instance

    def __init__(self):
        # 判断是否执行过初始化动作
        if Singleton.flag:
            return

        #  修改类属性的标记
        Singleton.flag = True

    def transIp2Loc(self):
        reader = geoip2.database.Reader(settings.GEOLITE_CITY_DB)
        return reader


my_geo_reader = Singleton().transIp2Loc()


def get_location(ip):
    try:
        response = my_geo_reader.city(ip)
        if response.country.iso_code:
            loc = response.country.iso_code
        else:
            loc = ''
        return loc
    except:
        return ''


def get_client_ua(request):
    return request.META.get('HTTP_USER_AGENT', '')


def page2offset(page, size,
                max_size=_MAX_PAGE_SIZE, default_size=_DEFAULT_PAGE_SIZE):
    '''return limit, offset
    '''
    limit = default_size if not size or size > max_size else size
    if not page or page < 1:
        page = 1
    offset = 0 if not page else (page - 1) * limit
    return limit, offset


class Struct:

    def __init__(self, **entries):
        self.__dict__.update(entries)


def dict2obj(dct):
    s = Struct(**dct)

    return s


_COM_P = re.compile(
    r'\[aid:(.*)\],\[code:(.*)\],\[lan:(.*)\],\[svc:(.*)\],\[svn:(.*)\],\[cvn:(.*)\],\[cvc:(.*)\],\[chn:(.*)\]')


def parse_p(p):
    if not p:
        return {}
    g = _COM_P.match(p)
    if not g:
        return {}
    else:
        return {
            'aid': g.group(1),
            'code': g.group(2),
            'lan': g.group(3),
            'svc': g.group(4),
            'svn': g.group(5),
            'cvn': g.group(6),
            'cvc': int(g.group(7)),  # app version
            'chn': g.group(8)  # channel: ios or android channel
        }


def parse_common_params(query_dct):
    page = int(query_dct.get('page', 1))
    size = int(query_dct.get('size', 0))
    return page, size


def check_agent_params(params, required_params,  # 必须值
                       default_param_dct=None,  # 默认值
                       param_type_dct=None,  # 参数类型，GET一般需要强制转换
                       param_validfunc_dct=None):  # 参数合法判定
    '''验证传入参数有效性，注意这里会对params做in-place修改
    '''
    for param in required_params:
        if param not in params:
            if param == 'unionpay_no':
                raise_with_traceback(ParamError('缺少->%s' % '银联号'))
            elif param == 'account_city':
                raise_with_traceback(ParamError('缺少->%s' % '银行卡归属城市'))
            elif param == 'account_province':
                raise_with_traceback(ParamError('缺少->%s' % '银行卡归属省份'))
            elif param == 'identity_card':
                raise_with_traceback(ParamError('缺少->%s' % '持卡人身份证号'))
            elif param == 'bank_name':
                raise_with_traceback(ParamError('缺少->%s' % '支行信息'))
            elif param == 'phone_number':
                raise_with_traceback(ParamError('缺少->%s' % '电话号'))
            elif param == 'account_name':
                raise_with_traceback(ParamError('缺少->%s' % '持卡人姓名'))
            elif param == 'card_no':
                raise_with_traceback(ParamError('缺少->%s' % '银行卡号'))
            elif param == 'bank_code':
                raise_with_traceback(ParamError('缺少->%s' % '银行'))
            elif param == 'total_fee':
                raise_with_traceback(ParamError('缺少->%s' % '金额'))
            else:
                raise_with_traceback(ParamError('缺少->%s' % param))

    if default_param_dct:
        for param in default_param_dct:
            if param not in params:
                params[param] = default_param_dct[param]

    if param_type_dct:
        for field, t in param_type_dct.iteritems():
            if field in params:
                try:
                    if t is basestring:
                        t = smart_unicode
                    params[field] = t(params[field])
                except Exception:
                    raise_with_traceback(ParamError(
                        'param %s type wrong' % field))

    if param_validfunc_dct:
        for field, func in param_validfunc_dct.iteritems():
            if field in params:
                try:
                    assert func(params[field])
                except AssertionError:
                    raise_with_traceback(ParamError(
                        'param %s illegal' % field))
